package emon.eline.manage.media.service;

import com.alibaba.fastjson.JSON;
import emon.eline.exception.ExceptionCast;
import emon.eline.manage.media.config.RabbitMQConfig;
import emon.eline.manage.media.dao.MediaFileRepository;
import emon.eline.manage.media.dto.CheckChunkResult;
import emon.eline.manage.media.dto.MediaCode;
import emon.eline.manage.media.entity.MediaFile;
import emon.eline.model.CommonCode;
import emon.eline.model.ResponseResult;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.*;

@Service
public class MediaUploadService {
    @Autowired
    private MediaFileRepository mediaFileRepository;

    @Value("${xc-service-manage-media.upload-location}")
    String uploadLocation;
    //视频处理路由
    @Value("${xc-service-manage-media.mq.routingkey-media-video}")
    public  String routingkey_media_video;
    @Autowired
    private RabbitTemplate rabbitTemplate;

    private String getFileFloderPath(String fileMd5) {
        return uploadLocation + fileMd5.substring(0, 1) + File.separator
                + fileMd5.substring(1, 2)+ File.separator + fileMd5 + File.separator;
    }

    private String getFilePath(String fileMd5, String fileExt) {
        return uploadLocation + fileMd5.substring(0, 1) + File.separator
                + fileMd5.substring(1, 2)+ File.separator + fileMd5 + File.separator + fileMd5 + "." + fileExt;
    }

    private String getChunkFileFolderPath(String fileMd5) {
        return uploadLocation + fileMd5.substring(0, 1) + File.separator
                + fileMd5.substring(1, 2)+ File.separator + fileMd5 + File.separator + "chunks" + File.separator;
    }

    /**
     * 根据文件md5得到文件路径
     * 规则：
     * 一级目录：md5的第一个字符
     * 二级目录：md5的第二个字符
     * 三级目录：md5
     * 文件名：md5+文件扩展名
     *
     * @param fileMd5 文件md5值
     * @param fileExt 文件扩展名
     * @return 文件路径
     */
    public ResponseResult register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        String filePath = getFilePath(fileMd5, fileExt);
        File file = new File(filePath);
        Optional<MediaFile> byId = mediaFileRepository.findById(fileMd5);
        if (file.exists() && byId.isPresent()) {
            // 文件已存在
            ExceptionCast.cast(MediaCode.UPLOAD_FILE_REGISTER_EXIST);
        }
        File folder = new File(getFileFloderPath(fileMd5));
        if (!folder.exists()) {
            folder.mkdirs();
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }


    public CheckChunkResult checkchunk(String fileMd5, Integer chunk, Integer chunkSize) {
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFile = new File(chunkFileFolderPath + chunk);
        if (!chunkFile.exists()) {
            return new CheckChunkResult(CommonCode.SUCCESS, true);
        } else {
            return new CheckChunkResult(CommonCode.SUCCESS, false);
        }
    }

    public ResponseResult uploadchunk(MultipartFile file, String fileMd5, Integer chunk) {
        String chunkFileFolderPath = getChunkFileFolderPath(fileMd5);
        File chunkFile = new File(chunkFileFolderPath);
        if (!chunkFile.exists()) {
            chunkFile.mkdirs();
        }
        String chunkFilePath = chunkFileFolderPath + chunk;
//        InputStream inputStream = null;
        try (InputStream inputStream = file.getInputStream();
             FileOutputStream fileOutputStream = new FileOutputStream(new File(chunkFilePath))) {
            IOUtils.copy(inputStream, fileOutputStream);
        } catch (IOException e) {
            ExceptionCast.cast(CommonCode.FAIL);
        }
        return new ResponseResult(CommonCode.SUCCESS);
    }

    public ResponseResult mergechunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        // 1.合并所有分块
        // 得到分块文件的属目录
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);
        List<File> fileLists = Arrays.asList(chunkFileFolder.listFiles());
        String filePath = this.getFilePath(fileMd5, fileExt);
        File mergeFile = new File(filePath);
        // 执行合并文件
        mergeFile = this.mergeFile(fileLists, mergeFile);
        if (null == mergeFile) {
            ExceptionCast.cast(MediaCode.MERGE_FILE_FAIL);
        }
        boolean b = this.checkFileMd5(mergeFile, fileMd5);
        if (!b) {
            ExceptionCast.cast(MediaCode.MERGE_FILE_CHECKFAIL);
        }
        // 将文件按的信息写入mongodb中
        MediaFile mediaFile = new MediaFile();
        mediaFile.setFileId(fileMd5);
        mediaFile.setFileOriginalName(fileName);
        mediaFile.setFileName(fileMd5 + "." + fileExt);
        String path = fileMd5.substring(0, 1) + File.separator + fileMd5.substring(1, 2) +
                File.separator + fileMd5 + File.separator;
        mediaFile.setFilePath(path);
        mediaFile.setFileSize(fileSize);
        mediaFile.setUploadTime(new Date());
        mediaFile.setMimeType(mimetype);
        mediaFile.setFileType(fileExt);
        mediaFile.setFileStatus("301002");
        mediaFileRepository.save(mediaFile);
        sendProcessVideoMsg(mediaFile.getFileId());
        return new ResponseResult(CommonCode.SUCCESS);
    }
    // 发送视频处理消息
    public ResponseResult sendProcessVideoMsg(String mediaId){
        Optional<MediaFile> byId = mediaFileRepository.findById(mediaId);
        if (!byId.isPresent()) {
            ExceptionCast.cast(CommonCode.FAIL);
        }
        // 构建消息内容
        HashMap<Object, Object> map = new HashMap<>();
        map.put("mediaId",mediaId);
        String string = JSON.toJSONString(map);
        try {
            rabbitTemplate.convertAndSend(RabbitMQConfig.EX_MEDIA_PROCESSTASK,routingkey_media_video,string);
        } catch (AmqpException e) {
            e.printStackTrace();
            return new ResponseResult(CommonCode.FAIL);
        }

        return new ResponseResult(CommonCode.SUCCESS);
    }
    // 校验文件
    private boolean checkFileMd5(File mergeFile,String md5){
        try (FileInputStream inputStream = new FileInputStream(mergeFile)){
            String md5Hex = DigestUtils.md5Hex(inputStream);
            if (md5.equalsIgnoreCase(md5Hex)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }
    // 合并文件
    private File mergeFile(List<File> chunkFileList, File mergeFile) {
        try {
            if (mergeFile.exists()) {
                mergeFile.delete();
            } else {
                mergeFile.createNewFile();
            }
            Collections.sort(chunkFileList,(f1,f2)->{
                if(Integer.parseInt(f1.getName()) > Integer.parseInt(f2.getName())){
                    return 1;
                }else{
                    return -1;
                }
            });
            RandomAccessFile writer = new RandomAccessFile(mergeFile,"rw");
            byte[] b = new byte[1024];
            for (File file : chunkFileList) {
                RandomAccessFile reader = new RandomAccessFile(file,"r");
                int len =-1;

                while((len = reader.read(b)) != -1){
                    writer.write(b,0,len);
                }
                reader.close();
            }
            writer.close();

        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        return mergeFile;
    }
}
